package com.hup.utils.swing.view.colorChosePanel;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.MemoryImageSource;


/**panel of color chooser*/
public abstract class ColorChooserPanel1602 extends JPanel {
	private static final long serialVersionUID = 469767385142573966L;
	
	/* ==== base UI param ==== */
	private ColorLabelA clA;
	private ColorLabelB clB;
	private ColorLabelC clC;
	
	/**RadioButton of RGB, with ItemListener, set mode when item changed.*/
	private JRadioButton bRed,bGreen,bBlue;
	/**slider of RGB, with ChangeListener,set colorLabel(cursor&paint) & textField.text(if flag=true) & set color of preview label when value changed*/
	private JSlider slRed,slGreen,slBlue;
	/**textField to show or set RGB value, setText passive when slider changed | setText&(flag→false→avoid slider's... →true) active when manual */
	private JTextField tRed,tGreen,tBlue;

	/* ==== base state param ==== */
	/**mode of ColorA(1inRGB)*/
	private int mode;
	/** whether set textField when adjust slider value*/
	private boolean isSetText = true;
	private String textTemp;
	
	
	/* ============ construction methods ========== */
	/**panel of color chooser,  
	 * @param c default color */
	public ColorChooserPanel1602(Color c) {
		super(null);
		
		setUI();
		setExtendUI();
		setActionPerformers();
		
		bRed.setSelected(true);
		setColor(c);
	}

	/* ============ UI construction methods ========== */
	private void setUI() {
		//try {
		//	UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
		//} catch (Throwable e) {}
		
		add(clA = new ColorLabelA(0));
		add(clB = new ColorLabelB(0, 255));
		add(clC = new ColorLabelC());

		add(bRed = new JRadioButton("红色(R)"));
		add(bGreen = new JRadioButton("绿色(G)"));
		add(bBlue = new JRadioButton("蓝色(B)"));
		add(slRed = new JSlider(0, 255));
		add(slGreen = new JSlider(0, 255));
		add(slBlue = new JSlider(0, 255));
		add(tRed = new JTextField());
		add(tGreen = new JTextField());
		add(tBlue = new JTextField());
		
		clA.setBounds(5, 5, 185, 20);
		clB.setBounds(5, 28, 185, 185);
		clC.setBounds(5, 215, 185, 42);
		bRed.setBounds(200, 5, 90, 30);
		bGreen.setBounds(200, 45, 90, 30);
		bBlue.setBounds(200, 85, 90, 30);
		slRed.setBounds(270, 5, 255, 30);
		slGreen.setBounds(270, 45, 255, 30);
		slBlue.setBounds(270, 85, 255, 30);
		tRed.setBounds(530, 5, 55, 30);
		tGreen.setBounds(530, 45, 55, 30);
		tBlue.setBounds(530, 85, 55, 30);
		
		String text = "<html>"
				+ " 滑动滚轮: 设换 颜色级别A.<br>"
				+ " 右键 (点击 或 拖拽): 设置 颜色级别A.<br>"
				+ " 左键 (点击 或 拖拽): 设置 颜色级别B.<br>"
				;
		clA.setToolTipText("- - A.");
		clB.setToolTipText(text+"- - B.");
		clC.setToolTipText("左键 (点击): 设置颜色 - - C.");
		ToolTipManager.sharedInstance().setDismissDelay(10000);
	}

	/**
	 * source : action
	 * RB selected : update other RB state, CL mode&value
	 * CL mouseAction : update SL value
	 * SL changed : update TF(if isSetText), CL mode&value & Cursor
	 * TF input : update SL & (isSetText = false),
	 * main changing entry : SL.
	 * */
	private void setActionPerformers() {
		setRadioButtonListener();
		setColorLabelListener();
		setSliderListener();
		setTextFieldListener();
	}
	
	/* ============ UI actionPerformer methods ========== */
	private void setRadioButtonListener() {
		ItemListener il = new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				JRadioButton b = (JRadioButton) e.getSource();
				if(
						false == bRed.isSelected()
						&& bRed.isSelected() == bGreen.isSelected()
						&& bGreen.isSelected() == bBlue.isSelected()
						) {
					b.setSelected(true);
					return;
				}
				if(!b.isSelected()) return;
				//pause.s(e);
				if(b == bRed) {
					mode = 0;
					bGreen.setSelected(false);
					bBlue.setSelected(false);
					clB.changeMode(mode, slRed.getValue());
					clA.changeMode(mode, slRed.getValue());
				} else if(b == bGreen) {
					mode = 1;
					bRed.setSelected(false);
					bBlue.setSelected(false);
					clB.changeMode(mode, slGreen.getValue());
					clA.changeMode(mode, slGreen.getValue());
				} else if(b == bBlue) {
					mode = 2;
					bRed.setSelected(false);
					bGreen.setSelected(false);
					clB.changeMode(mode, slBlue.getValue());
					clA.changeMode(mode, slBlue.getValue());
				}
				//pause.s("mode",mode);
			}
		};

		bRed.addItemListener(il);
		bGreen.addItemListener(il);
		bBlue.addItemListener(il);
	}

	private void setColorLabelListener() {
		MouseAdapter ma = new MouseAdapter() {
			private boolean isDragA;
			private boolean isDragB;
			public void mousePressed(MouseEvent e) {
				if(e.getSource() == clC) {
					clCPerformed(e);
					return;
				}
				if(e.getButton() == 3) {
					isDragA = true;
					get1inRGB(e);
				} else if(e.getButton() == 1) {
					isDragB = true;
					get2inRGB(e,true);
				}
				clB.requestFocus();//解决textField失去焦点
			}

			public void mouseReleased(MouseEvent e) {
				if(e.getButton() == 3) {
					isDragA = false;
				} else if(e.getButton() == 1) {
					isDragB = false;
				}
			}

			public void mouseDragged(MouseEvent e){
				//pause.s(e.getButton());//===0
				if(isDragB) get2inRGB(e,true);
				else if(isDragA) get1inRGB(e);
			}
			
			public void mouseWheelMoved(MouseWheelEvent e) {
				switch (mode+e.getWheelRotation()) {
				case 0:case 3:
					bRed.setSelected(true);
					break;
				case 1:
					bGreen.setSelected(true);
					break;
				case 2:case -1:
					bBlue.setSelected(true);
					break;
				}
			}

			private void get1inRGB(MouseEvent e) {
				int valueA = (int) (256.0*e.getX()/clB.width);
				if(valueA<0) valueA=0;
				else if(valueA>255) valueA=255;

				switch (mode) {
				case 0:
					slRed.setValue(valueA);
					break;
				case 1:
					slGreen.setValue(valueA);
					break;
				case 2:
					slBlue.setValue(valueA);
					break;
				}
			}

			private void get2inRGB(MouseEvent e,boolean isUpDataSlider) {
				int x = (int) (256.0*e.getX()/clB.width);//value of 2inRGB
				int y = (int) (256.0*e.getY()/clB.height);

				if(x<0) x=0;
				else if(x>255) x=255;
				if(y<0) y=0;
				else if(y>255) y=255;
				//pause.s(x,y);

				int red = (clB.pix[y*256+x]>>16)&0xff;
				int green = (clB.pix[y*256+x]>>8)&0xff;
				int blue = (clB.pix[y*256+x])&0xff;

				//pause.s(red,green,blue);
				upDate2inRGB(red,green,blue);
			}

			private void upDate2inRGB(int red, int green, int blue) {
				//pause.s("mode2",mode);
				switch (mode) {
				case 0:
					slGreen.setValue(green);
					slBlue.setValue(blue);
					break;
				case 1:
					slRed.setValue(red);
					slBlue.setValue(blue);
					break;
				case 2:
					slRed.setValue(red);
					slGreen.setValue(green);
					break;
				}
			}
			
			private void clCPerformed(MouseEvent e) {
				int x = (int) (9.0*e.getX()/clC.width);//value of 2inRGB
				int y = (int) (3.0*e.getY()/clC.height);
				
				if(x>8) x=8;
				if(y>2) y=2;
				//pause.s(x,y);
				
				int red = (clC.pix[y*9+x]>>16)&0xff;
				int green = (clC.pix[y*9+x]>>8)&0xff;
				int blue = (clC.pix[y*9+x])&0xff;
				//pause.s(red,green,blue);
				
				slRed.setValue(red);
				slGreen.setValue(green);
				slBlue.setValue(blue);
			}
		};
		clB.addMouseListener(ma);
		clB.addMouseMotionListener(ma);
		clB.addMouseWheelListener(ma);
		clC.addMouseListener(ma);
	}

	private void setSliderListener() {
		ChangeListener cl = new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				//pause.s("slC");
				JSlider sl = (JSlider) e.getSource();
				if(sl == slRed) {
					if(isSetText) tRed.setText(sl.getValue()+"");

					if(mode == 0) {
						clB.changeMode(mode, sl.getValue());
						clA.changeMode(mode, sl.getValue());
						clA.upDataCursorX(sl.getValue());
					}
					else clB.upDataCursorX(sl.getValue());
				} else if(sl == slGreen) {
					if(isSetText) tGreen.setText(sl.getValue()+"");

					if(mode == 1) {
						clB.changeMode(mode, sl.getValue());
						clA.changeMode(mode, sl.getValue());
						clA.upDataCursorX(sl.getValue());
					}
					else if(mode == 0) clB.upDataCursorX(sl.getValue());
					else if(mode == 2) clB.upDataCursorY(sl.getValue());
				} else if(sl == slBlue) {
					if(isSetText) tBlue.setText(sl.getValue()+"");

					if(mode == 2) {
						clB.changeMode(mode, sl.getValue());
						clA.changeMode(mode, sl.getValue());
						clA.upDataCursorX(sl.getValue());
					}
					else clB.upDataCursorY(sl.getValue());
				}
				
				changedPerformed();
			}
		};

		slRed.addChangeListener(cl);
		slGreen.addChangeListener(cl);
		slBlue.addChangeListener(cl);
	}

	private void setTextFieldListener() {
		KeyListener kl = new KeyListener() {
			public void keyTyped(KeyEvent e) {}
			public void keyReleased(KeyEvent e) {
				if(e.getKeyCode() == KeyEvent.VK_ENTER) {
					clB.requestFocus();
				} else if(e.getKeyCode() == KeyEvent.VK_ESCAPE) {
					((JTextField) e.getSource()).setText(textTemp);
					clB.requestFocus();
				}
			}
			public void keyPressed(KeyEvent e) {} 
		};
		
		FocusListener fl = new FocusListener() {
			public void focusLost(FocusEvent e) {
				JTextField t = (JTextField) e.getSource();
				//pause.s("LF"+t.getText());
				
				int v = Integer.parseInt(t.getText());
				isSetText = false;
				if(t == tRed) {
					slRed.setValue(v);
				} else if(t == tGreen) {
					slGreen.setValue(v);
				} else if(t == tBlue) {
					slBlue.setValue(v);
				}
				isSetText = true;
			}
			
			public void focusGained(FocusEvent e) {
				JTextField t = (JTextField) e.getSource();
				textTemp = t.getText();
				//pause.s("GF"+t.getText(),textTemp);
				t.select(0, 3);
			}
		};
		
		tRed.addKeyListener(kl);
		tGreen.addKeyListener(kl);
		tBlue.addKeyListener(kl);
		tRed.addFocusListener(fl);
		tGreen.addFocusListener(fl);
		tBlue.addFocusListener(fl);
		tRed.setDocument(new TFDocument());
		tGreen.setDocument(new TFDocument());
		tBlue.setDocument(new TFDocument());
	}
	
	/* ============== private frame function class ================== */
	/* ============== private frame function class ================== */
	/**label class of color level A, 1inRGB = [0,255], in int[256]*/
	private class ColorLabelA extends JLabel {
		private static final long serialVersionUID = 3258365933251687167L;
		
		/* ======== base param ======= */
		/**the array of integers which are to be used to hold the RGB pixels*/
		private int[] pix = new int[256];
		/** size of Label*/
		private int width,height;
		private Image img;

		/* ======== state param ======= */
		/**current modeValue(of RGB) position of Cursor*/
		private int cX;

		/**mode of RGB*/
		public ColorLabelA(int mode) {
			changeMode(mode,0);
		}

		public void changeMode(int mode,int value) {
			switch (mode) {
			case 0://red
				for(int j=0;j<256;j++)
					pix[j] = 255<<24|j<<16|0<<8|0;//AARRGGBB
				break;
			case 1://green
				for(int j=0;j<256;j++)
					pix[j] = 255<<24|0<<16|j<<8|0;//AARRGGBB
				break;
			case 2://blue
				for(int j=0;j<256;j++)
					pix[j] = 255<<24|0<<16|0<<8|j;//AARRGGBB
				break;
			}
			img = createImage(new MemoryImageSource(256, 1, pix, 0, 256));
			upDataCursorX(value);
		}

		/**upDataCursor[x] x=modeValue*/
		public void upDataCursorX(int x) {
			cX = (int) (x/256.0*width)+1;
			repaint();
		}

		public void paint(Graphics g) {
			super.paint(g);
			g.drawRect(0, 0, width+1, height+1);
			g.drawImage(
					img,
					1,1,width,height,//of g-com
					//0,0,256,1,//of img
					null);

			g.setColor(Color.WHITE);
			g.drawLine(cX, 3, cX, height);//col
		}

		public void setBounds(int x, int y, int width, int height) {
			super.setBounds(x, y, width, height);
			this.width = width-2;
			this.height = height-2;
		}
	}

	/**label class of color level B, 2inRGB = [0,255], in int[256*256]<pre>
	 * clA	R	G	B
	 * clB →	G	R	R
	 *     ↓	B	B	G
	 * */
	private class ColorLabelB extends JLabel {
		private static final long serialVersionUID = -2595867549265296263L;
		
		/* ======== base param ======= */
		/**the array of integers which are to be used to hold the RGB pixels*/
		public int[] pix = new int[256*256];
		/** size of Label*/
		public int width,height;
		private Image img;

		/* ======== state param ======= */
///**current 1inRGB value of mode*/
//public int modeValue;
		/**current RGB position of Cursor*/
		private int cX,cY;

		/**mode: R,G,B == 0~2*/
		public ColorLabelB(int mode,int value) {
			changeMode(mode, value);
		}

		/**mode: R,G,B == 0~2*/
		public void changeMode(int mode,int value) {
			//modeValue = value;
			switch (mode) {
			case 0://red
				for(int i=0;i<256;i++)//↓-B[0~255]
					for(int j=0;j<256;j++)//→-G[0~255]
						pix[i*256+j] = 255<<24|value<<16|j<<8|i;//AARRGGBB
				break;
			case 1://green
				for(int i=0;i<256;i++)//↓-B[0~255]
					for(int j=0;j<256;j++)//→-R[0~255]
						pix[i*256+j] = 255<<24|j<<16|value<<8|i;
				break;
			case 2://blue
				for(int i=0;i<256;i++)//G
					for(int j=0;j<256;j++)//R
						pix[i*256+j] = 255<<24|j<<16|i<<8|value;
				break;
			}
			img = createImage(new MemoryImageSource(256, 256, pix, 0, 256));
			repaint();
		}

		public void upDataCursorX(int x) {
			cX = (int) (x/256.0*width)+1;
			repaint();
		}

		public void upDataCursorY(int y) {
			cY = (int) (y/256.0*height)+1;
			repaint();
		}

		public void paint(Graphics g) {
			super.paint(g);
			g.drawRect(0, 0, width+1, height+1);
			g.drawImage(
					img,
					1,1,width,height,
					null);

			g.drawLine(cX-10, cY, cX-2, cY);//row
			g.drawLine(cX+2, cY, cX+10, cY);//row
			g.drawLine(cX, cY-10, cX, cY-2);//col
			g.drawLine(cX, cY+2, cX, cY+10);//col
			
		}

		public void setBounds(int x, int y, int width, int height) {
			super.setBounds(x, y, width, height);
			this.width = width-2;
			this.height = height-2;
		}
	}
	
	private class ColorLabelC extends JLabel {
		private static final long serialVersionUID = 5406797653950130855L;
		
		public int[] pix = new int[27];
		/** size of Label*/
		public int width,height;
		private Image img;
		
		public ColorLabelC() {
			initImg();
		}
		
		/**
		 *   r0 ...  rI/2 ...  rI
		 *  → g0~I
		 *  ↓ b0~I
		 * */
		private void initImg() {
			int r=0,g=0,b=0;
			for(int i=1; i<=3*3*3; i++) {
				pix[i-1] = 255<<24
						|((int)(r*255/2f))<<16
						|((int)(g*255/2f))<<8
						|((int)(b*255/2f));
				//System.out.print(r+"-"+g+"-"+b+",");
				
				if(i%3 == 0) r++;
				if(i%9 == 0) {
					r = 0;
					if(i != 0) b++;
					//System.out.print("\n");
				}
				
				g++;
				if(g == 3) {
					g = 0;
					//if(i%9 != 0) System.out.print(" || ");
				}
			}
			img = createImage(new MemoryImageSource(9, 3, pix, 0, 9));
		}

		public void paint(Graphics g) {
			super.paint(g);
			g.drawRect(0, 0, width+1, height+1);
			
			g.drawImage(
					img,
					1,1,width,height,
					null);
		}
		
		public void setBounds(int x, int y, int width, int height) {
			super.setBounds(x, y, width, height);
			this.width = width-2;
			this.height = height-2;
		}

	}
	
	private class TFDocument extends PlainDocument {
		private static final long serialVersionUID = 6746450195332974140L;

		public void insertString(int offs, String str, AttributeSet a)
				throws BadLocationException {
			//pause.s("D:"+offs,str);
			if(getLength() + str.length() > 3) {
				Toolkit.getDefaultToolkit().beep();
				return;
			}
			
			int c;
			for (int i=str.length()-1; i >= 0; i--) {
				c = str.charAt(i);
				//pause.s("insert:"+c,( c>='0'&& c<='9' ),( c>='A' && c<='Z'),( c>='a' && c<='z'));
				if(!( c>='0'&& c<='9' )) {
					Toolkit.getDefaultToolkit().beep();
					return; 
				}
			}
			super.insertString(offs, str, a);
			if(getLength() == 3 && Integer.parseInt(getText(0, 3)) > 255 ) {
				Toolkit.getDefaultToolkit().beep();
				replace(0, 3, "255", null);
			}
		}

	}
	
	private class ColorDialog extends JDialog {
		private static final long serialVersionUID = 8939055292840566825L;

		private ColorDialog(Window owner, String title) {
			super(owner,title);
			setModalityType(DEFAULT_MODALITY_TYPE);
			setUI();
			setVisible(true);
		}
		
		private void setUI() {
			setSize(600, 290);
			setResizable(false);
			setLocationRelativeTo(null);
			
			setContentPane(ColorChooserPanel1602.this);
		}
		
		public void setVisible(boolean b) {
			super.setVisible(b);
			if(!b) this.dispose();
		}
	}
	
	
	/* ============== public methods ================== */
	/** set extend UI */
	public abstract void setExtendUI();
	
	/** action after changed color state */
	public abstract void changedPerformed();
	
	/** get current color */
	public Color getColor() {
		return new Color(slRed.getValue(),slGreen.getValue(),slBlue.getValue());
	}
	
	/** set current color */
	public void setColor(Color c) {
		slRed.setValue(c.getRed());
		slGreen.setValue(c.getGreen());
		slBlue.setValue(c.getBlue());
	}
	
	public void creatDialog(Window owner, String title) {
		new ColorDialog(owner, title);
	}
	
	/* ============ test methods ==========
	public static void main(String[] args) {
		PColorChooserPanel c = new PColorChooserPanel(new Color(58,208,47)) {
			private static final long serialVersionUID = 1L;

			private JRadioButton cbBG, cbFont;
			
			public void setExtendUI() {
				add(cbBG = new JRadioButton("backGround"));
				add(cbFont = new JRadioButton("Font"));
				
				cbBG.setBounds(200, 130, 100, 50);
				cbFont.setBounds(300, 130, 100, 50);
				
				ButtonGroup bg = new ButtonGroup();
				bg.add(cbBG);
				bg.add(cbFont);
				cbBG.setSelected(true);
				cbBG.setOpaque(true);
				cbFont.setOpaque(true);
			}

			public void changedPerformed() {
				//repaint();
				if(cbBG.isSelected()) cbBG.setBackground(this.getColor());
				if(cbFont.isSelected()) cbFont.setBackground(this.getColor());
			}
		};
		c.creatDialog(null);
		System.exit(0);
	}/**/
	
}
